from kubernetes import client, config
import utils
import threading
from configloader import ConfigLoader


cl = ConfigLoader.get_instance()

class ClusterCommunication:

    _instance_lock = threading.Lock()

    def __init__(self, debug_enable=0):
        config.load_kube_config()
        self.core_api_v1 = client.CoreV1Api()
        self.apps_api_v1 = client.AppsV1Api()
        print("Cluster connected successfully!")

    @classmethod
    def get_instance(cls, *args, **kwargs):
        if not hasattr(ClusterCommunication, "_instance"):
            with ClusterCommunication._instance_lock:
                if not hasattr(ClusterCommunication, "_instance"):
                    ClusterCommunication._instance = ClusterCommunication(*args, **kwargs)
        return ClusterCommunication._instance

    def scale_deployment_replicas(self, deployment_name, ns_name="default", replicas=1):
        if (deployment_name == None) or (deployment_name == ""):
            print("error: missing scale deployment name")
            return 0
        before_resp = self.apps_api_v1.read_namespaced_deployment(name=deployment_name, namespace=ns_name)
        if before_resp.spec.replicas == replicas:
            return 0
        before_resp.spec.replicas = replicas
        ret = self.apps_api_v1.replace_namespaced_deployment(name=deployment_name, namespace=ns_name,
                                                             body=before_resp)
        if ret:
            return 0
        return 1

    def list_services(self):
        result = []
        ret = self.core_api_v1.list_service_for_all_namespaces(watch=False)
        for i in ret.items:
            info = {'kind': i.kind, 'namespace': i.metadata.namespace, 'name': i.metadata.name,
                    'ip': i.spec.cluster_ip, 'port': i.spec.ports[0].target_port}
            result.append(info)
        return result

    def get_prometheus_address_and_port(self):
        if cl.debug == 0:
            return cl.prometheus_address + ":" + cl.prometheus_port
        svc = self.list_services()
        for i in svc:
            if i['name'] in 'prometheus-server':
                return i['ip'] + ':' + str(i['port'])
        return "127.0.0.1:9090"

    def list_all_pods(self):
        print("Listing pods with their IPs:")
        ret = self.core_api_v1.list_pod_for_all_namespaces(watch=False)
        for item in ret.items:
            print(
                "%s\t%s\t%s" %
                (item.status.pod_ip,
                 item.metadata.namespace,
                 item.metadata.name))

    def get_all_nodes_resources(self):
        ret = self.core_api_v1.list_node()
        # millcores --- cpu = 100m
        redundancy = 300
        total_resources = 0
        for item in ret.items:
            if cl.nodes.get(item.metadata.name) == 1:
                total_resources += int(item.status.capacity['cpu']) * 1000
            else:
                continue
        print('total resources = millcores', total_resources)
        return total_resources - redundancy

    def get_pods_request_cpu_resources_map(self, namespace="default"):
        ret = self.apps_api_v1.list_deployment_for_all_namespaces()
        pods_request_resources = {}
        for item in ret.items:
            if item.metadata.namespace == namespace:
                containers = item.spec.template.spec.containers
                need = 0
                for c in containers:
                    if c.resources.requests:
                        millcores = c.resources.requests['cpu']
                        value = int(millcores.split('m')[0])
                        need += value
                pods_request_resources[item.metadata.name] = need
        return pods_request_resources

    def list_all_deployments(self, namespace="default"):
        ret = self.apps_api_v1.list_deployment_for_all_namespaces()
        deployments = []
        for item in ret.items:
            if item.metadata.namespace == namespace:
                deployments.append(item)
        return deployments

    def get_ip_map_service(self, namespace="default"):
        ret = self.core_api_v1.list_pod_for_all_namespaces()
        svc = {}
        for item in ret.items:
            if item.metadata.namespace == namespace and item.metadata.labels.get('app'):
                key = item.status.pod_ip
                config_name = item.spec.volumes[0].config_map.name
                value = utils.translate_config_name_to_deployment_name(config_name)
                svc[key] = value
        return svc

    def plan_cpu_resources_by_weights(self, weights):
        total_cpu_millcores = self.get_all_nodes_resources()
        total_weight = 0
        plan_resources = {}
        for k in weights:
            total_weight += weights[k]
        for k in weights:
            v = weights[k]
            plan_resources[k] = int(total_cpu_millcores * v) / total_weight
        return plan_resources

    def will_over_load(self, cnext):
        cpu_map = self.get_pods_request_cpu_resources_map()
        total = self.get_all_nodes_resources()
        cal = 0
        for c in cnext:
            for k in c:
                cal += int(cpu_map[k]) * c[k]
        print("cluster load is ", cal)
        return cal >= total